using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Collections;
using Zsa.Emulation.Mappers;

namespace Zsa.Emulation.Cartridge
{
    /// <summary>
    /// Reads and writes iNes carts.
    /// </summary>
    public class iNesCartDumpReader : CartDumpReaderWriter
    {
        #region Fields
        /// <summary>
        /// The rom start offset without the a trainer.
        /// </summary>
        private const int _RomStartOffsetWithoutTrainer = 16;
        /// <summary>
        /// The 3 byte rom header with extra padded byte.
        /// Spells NES.
        /// </summary>
        public const int RomHeader = 462;

        #endregion Fields

        #region Constructor
        /// <summary>
        /// Constructor
        /// </summary>
        public iNesCartDumpReader()
            : base ( "iNes" )
        {
        }
        #endregion Constructor

        #region Methods
        /// <summary>
        /// Trys to read the rom information.
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="cartInfo"></param>
        /// <returns></returns>
        public override bool TryGetInfo(System.IO.FileStream stream, out CartridgeFileInfo cartInfo)
        {
            // Temp vars
            byte[] _Bytes = new byte[20];
            RomFlags _RomFlags;
            MapperTypeNumber _MapperType;
            int _VRomBanks,
                _RomBanks,
                _RamBanks,
                _RomOffset;
                
            // Go to beginning
            stream.Seek(0, SeekOrigin.Begin);
            // Read the header.
            stream.Read(_Bytes, 0, _Bytes.Length);
            // Check that the first 3 bytes spell out nes.
            if ( (_Bytes[0] | _Bytes[1] << 1 | _Bytes[2] << 2) != RomHeader )
            {
                cartInfo = null;
                return false;
            }
            // Read the number of rom banks.
            _RomBanks = _Bytes[4];

            // Read the number of vrom banks.
            _VRomBanks = _Bytes[5];

            //Read the rom flags.
            _RomFlags = (RomFlags)(_Bytes[6] & 0x0F);

            // Read the mapper low byte and hi half bytes.
            _MapperType = (MapperTypeNumber)(
                ((_Bytes[6] & 0xF0)>>4) |
                ((_Bytes[7] & 0xF0))
                );

            // Read the ram banks.  if 0 then change to 1.
            _RamBanks = _Bytes[8] == 0 ?
                1 : (int)_Bytes[8];

            //// Get the vs cart rom flag.
            //_RomFlags |= (RomFlags)(
            //        (_Bytes[7] & 1) == 1 ? RomFlags.VSCart : RomFlags.Unknown
            //        );
            // Get the pal rom flag.
            _RomFlags |= (RomFlags)(
                    (_Bytes[9] & 1) == 1 ? RomFlags.IsPal : RomFlags.Unknown
                    );
            // Set the rom offset depending on is there is a trainer.
            _RomOffset =
                (RomFlags.HasTrainer & _RomFlags) == RomFlags.HasTrainer ?
                _RomStartOffsetWithoutTrainer + 512 : _RomStartOffsetWithoutTrainer;

            FileInfo _FileInfo = new FileInfo(stream.Name);
            
            //Create the create info.           
            cartInfo = new CartridgeFileInfo(
                stream.Name,
                "iNes",
                Guid.Empty,
                //iNesCartDumpReader.CreateMD5(stream),
                (int)_FileInfo.Length,
                MapperFactory.GetMapperInfo(_MapperType),
                _RomBanks,
                _VRomBanks,
                _RamBanks,
                _RomFlags,
                _RomOffset,
                _RomOffset + (_RomBanks * 0x4000),
                0x4000,
                0x4000
            );

            return true;


            


        }
        

        /// <summary>
        /// Loads the cart.
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public override Cart Load(System.IO.FileStream stream)
        {
            CartridgeFileInfo _CartInfo;
            if (!TryGetInfo(stream, out _CartInfo))
            {
                throw new Exception();
            }
            return new Cart(_CartInfo);
        }

        /// <summary>
        /// Called when [write].
        /// </summary>
        /// <param name="cart">The cart.</param>
        /// <param name="stream">The stream.</param>
        protected override void OnWrite(Cart cart, Stream stream)
        {
            WriteHeader(cart.FileInfo, stream);
        }


        /// <summary>
        /// Writes the header.
        /// </summary>
        /// <param name="fileInfo">The file info.</param>
        /// <param name="stream">The stream.</param>
        protected void WriteHeader(CartridgeFileInfo fileInfo, Stream stream)
        {
            byte[] 
                _TempBytes = BitConverter.GetBytes(RomHeader),
                _Buffer = new byte[16];

            stream.Write(_Buffer, 0, 16);
        }
        #endregion Methods
    }
}
